/*
    Nios-sim - one simple NIOSII simulator only for personal interest and fun.
    Copyright (C) 2010  chysun2000@gmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include "public.h"
#include "io_device.h"
#include "jtag_uart.h"
#include "timer.h"
#include "uart_core.h"
#include "nor_flash.h"

static struct io_device * devices[] = {
	&jtag_uart_io_device,
	&timer_core,
	&uart_core,
	&nor_flash_core
};

#define DEVICES_COUNT (sizeof(devices)/sizeof(devices[0]))

static struct io_device * last_used_device = NULL;

struct io_device * get_device(uint32_t address)
{
	struct io_device * ret_val = NULL;
	uint32_t i = 0;
	
	if (last_used_device != NULL){
		if(last_used_device->is_belong(address) == ADDR_IS_DEV){
			return last_used_device;
		}
	}

	for (i=0;i<DEVICES_COUNT; i++){
		if (devices[i]->is_belong != NULL){
			if (devices[i]->is_belong(address) == ADDR_IS_DEV){
				ret_val = devices[i];
				last_used_device = ret_val;
				break;
			}
		}
	}
	
	return ret_val;
}

void init_devices(void)
{
	uint32_t i = 0;
	
	printf("--------------------------------------------------\n");
	printf(" Init H/W Device Module!\n");
	printf("--------------------------------------------------\n");
	for (i=0;i<DEVICES_COUNT; i++){
		if (devices[i]->init != NULL){
			devices[i]->init(devices[i]);
		}
	}
	printf("--------------------------------------------------\n");
}

static const uint32_t data_mask[5] = {
	[1] = 0xFF,
	[2] = 0xFFFF,
	[4] = 0xFFFFFFFF,
};

uint32_t io_write_data(uint32_t old_data, uint32_t new_data, 
							uint32_t data_len)
{
	uint32_t mask = data_mask[data_len];

	old_data = old_data & (~mask);
	new_data = new_data & mask;
	return (old_data | new_data);
}

uint32_t io_write_data_mask(uint32_t old_data, uint32_t new_data,
								uint32_t data_len, uint32_t valid_mask,
								 uint32_t only_read_mask)
{
	uint32_t mask = data_mask[data_len];
	
	new_data = new_data & mask; /* remove as the access bus width */
	new_data = new_data & valid_mask; /* remove as the valid bits */
	new_data = new_data & (~only_read_mask); /* remove the read-only bits */
	
	old_data = old_data &(~mask | only_read_mask);
	
	return (old_data | new_data);
}

uint32_t io_read_data(unsigned old_data, uint32_t data_len)
{
	uint32_t mask = data_mask[data_len];
	return (old_data & mask);
}

void hw_simulating(void)
{
	int i = 0;
	struct io_device * device = NULL;
	
	for (i=0;i<DEVICES_COUNT; i++){
		device = devices[i];
		if(device->simulate != NULL){
			device->simulate(device);
		}
	}
}

uint32_t get_io_irq_status(void)
{
	int i = 0;
	uint32_t irq_mask = 0;
	struct io_device * device = NULL;
	
	for (i=0;i<DEVICES_COUNT; i++){
		device = devices[i];
		if(device->has_irq != NULL){
			if (device->has_irq(device) == DEV_HAS_IRQ){
				irq_mask |= device->irq_enable_mask;
			}
		}
	}
	
	return irq_mask;
}

uint32_t check_reg_bit(uint32_t value, uint32_t mask)
{
	if ((value & mask) == mask){
		return SIM_TRUE;
	}
	else {
		return SIM_FALSE;
	}
}
/*--------------------------------------------------------------------------*/
